﻿#include "QIVtkSelectionPipeline.h"
/*
 * Copyright (C) 2024 Paweł Gilewicz, Krystian Fudali
 *
 * This file is part of the Mesh Generating Tool. (https://github.com/PawelekPro/MeshGeneratingTool)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

 // prevent disabling some MSVC warning messages by VTK headers
#include <Standard_WarningsDisable.hxx>
#include <Standard_WarningsRestore.hxx>
#include <vtkActor.h>
#include <vtkAppendPolyData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>

#include <IVtkOCC_Shape.hxx>
#include <TDF_Label.hxx>
#include <IVtkTools_DisplayModeFilter.hxx>
#include <IVtkTools_ShapeObject.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include "Core/RWTools.h"
#include <BRepTools.hxx>

IMPLEMENT_STANDARD_RTTIEXT(QIVtkSelectionPipeline, Standard_Transient)

//----------------------------------------------------------------------------
QIVtkSelectionPipeline::QIVtkSelectionPipeline( const TopoDS_Shape& theShape, const Standard_Integer theTag, int InDivisions)
    : _shape(theShape)
    , _shapeID(theTag)
    , _divisions(InDivisions)
    , m_renderer(nullptr)
    , m_position(0, 0, 0)
    , featureValue(0)
{
    // 组织filter以供使用
    _filterMap.Bind(Filter_DM_Shape, vtkSmartPointer<IVtkTools_DisplayModeFilter>::New());
    _filterMap.Bind(Filter_DM_Hili, vtkSmartPointer<IVtkTools_DisplayModeFilter>::New());
    _filterMap.Bind(Filter_DM_Sel, vtkSmartPointer<IVtkTools_DisplayModeFilter>::New());
    _filterMap.Bind(Filter_SUB_Hili, vtkSmartPointer<IVtkTools_SubPolyDataFilter>::New());
    _filterMap.Bind(Filter_SUB_Sel, vtkSmartPointer<IVtkTools_SubPolyDataFilter>::New());

    // 构建普通actor
    _actor = vtkSmartPointer<vtkActor>::New();
    IVtkOCC_Shape::Handle anIVtkShape = new IVtkOCC_Shape(_shape);
    anIVtkShape->SetId(theTag);
    _dataSource = vtkSmartPointer<IVtkTools_ShapeDataSource>::New();
    _dataSource->SetShape(anIVtkShape);

    _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    _mapper->SetInputConnection(_dataSource->GetOutputPort());
    _actor->SetMapper(_mapper);
    IVtkTools_ShapeObject::SetShapeSource(_dataSource, _actor);

    //vtkSmartPointer<vtkLookupTable> aColorTable = IVtkTools::InitLookupTable();
    //IVtkTools::InitShapeMapper(_mapper, aColorTable);

    //高亮actor，线框模式显示，用于高亮。
    IVtkTools_DisplayModeFilter* aDMFilterH = IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Hili));
    aDMFilterH->SetDisplayMode(IVtk_DisplayMode::DM_Wireframe);
    IVtkTools_SubPolyDataFilter* aSUBFilterH = IVtkTools_SubPolyDataFilter::SafeDownCast(_filterMap.Find(Filter_SUB_Hili));

    // No highlighting exists initially
    aSUBFilterH->SetInputConnection(_dataSource->GetOutputPort());
    aDMFilterH->SetInputConnection(aSUBFilterH->GetOutputPort());

    _hiliMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    _hiliMapper->SetInputConnection(aDMFilterH->GetOutputPort());

    // 构建高亮actor，不可选择。
    _hiliActor = vtkSmartPointer<vtkActor>::New();
    _hiliActor->SetPickable(0);
    _hiliActor->SetVisibility(1);
    _hiliActor->GetProperty()->SetColor(0, 0, 1.0);
    _hiliActor->GetProperty()->SetOpacity(1.f);
    _hiliActor->GetProperty()->SetPointSize(_actor->GetProperty()->GetPointSize() + 2);
    _hiliActor->GetProperty()->SetLineWidth(_actor->GetProperty()->GetLineWidth() + 1);

    // Set mapper for actor
    _hiliActor->SetMapper(_hiliMapper);
    _hiliMapper->ScalarVisibilityOff();

    //构建选择器的filter：线框+Sub_sel
    IVtkTools_DisplayModeFilter* aDMFilterS = IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Sel));
    aDMFilterH->SetDisplayMode(IVtk_DisplayMode::DM_Wireframe);
    IVtkTools_SubPolyDataFilter* aSUBFilterS = IVtkTools_SubPolyDataFilter::SafeDownCast(_filterMap.Find(Filter_SUB_Sel));

    // No highlighting exists initially
    aSUBFilterS->SetInputConnection(_dataSource->GetOutputPort());
    aDMFilterS->SetInputConnection(aSUBFilterS->GetOutputPort());

    _selMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    _selMapper->SetInputConnection(aDMFilterS->GetOutputPort());

    // 新建已选actor，此actor不可选择。
    _selActor = vtkSmartPointer<vtkActor>::New();
    _selActor->SetPickable(0);
    _selActor->SetVisibility(1);
    _selActor->GetProperty()->SetColor(1, 1.0, 0); // Green color for selection
    _selActor->GetProperty()->SetOpacity(1.f);
    _selActor->GetProperty()->SetPointSize(_hiliActor->GetProperty()->GetPointSize() + 2);
    _selActor->GetProperty()->SetLineWidth(_hiliActor->GetProperty()->GetLineWidth() + 1);

    // Set mapper for actor
    _selActor->SetMapper(_selMapper);
    _selMapper->ScalarVisibilityOff();
}

QIVtkSelectionPipeline::~QIVtkSelectionPipeline() 
{

}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::AddToRenderer(vtkRenderer* theRenderer)
{
    if (theRenderer == nullptr)
    {
        return;
    }

    m_renderer = theRenderer;

    theRenderer->AddActor(_actor);
    theRenderer->AddActor(_hiliActor);
    theRenderer->AddActor(_selActor);
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::RemoveFromRenderer(vtkRenderer* theRenderer)
{
    if (theRenderer == nullptr)
    {
        return;
    }

    m_renderer = nullptr;
    _actor->SetPickable(false);

	theRenderer->RemoveActor(_actor);
	theRenderer->RemoveActor(_hiliActor);
	theRenderer->RemoveActor(_selActor);

    _shape.Nullify();
    
	vtkSmartPointer<vtkRenderWindow> aWin = theRenderer->GetRenderWindow();
	if (aWin != nullptr)
	{
		_actor->ReleaseGraphicsResources(aWin);
		_hiliActor->ReleaseGraphicsResources(aWin);
		_selActor->ReleaseGraphicsResources(aWin);
	}
	Modified();
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::ClearHighlightFilters()
{
    this->GetHighlightFilter()->Clear();
    this->GetHighlightFilter()->SetDoFiltering(true);
    this->GetHighlightFilter()->Modified();
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::ClearSelectionFilters()
{
    this->GetSelectionFilter()->Clear();
    this->GetSelectionFilter()->SetDoFiltering(true);
    this->GetSelectionFilter()->Modified();
}

//----------------------------------------------------------------------------
IVtkTools_DisplayModeFilter*
QIVtkSelectionPipeline::GetDisplayModeFilter()
{
    return IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Shape));
}

//----------------------------------------------------------------------------
IVtkTools_SubPolyDataFilter*
QIVtkSelectionPipeline::GetHighlightFilter()
{
    return IVtkTools_SubPolyDataFilter::SafeDownCast(_filterMap.Find(Filter_SUB_Hili));
}

//----------------------------------------------------------------------------
IVtkTools_SubPolyDataFilter*
QIVtkSelectionPipeline::GetSelectionFilter()
{
    return IVtkTools_SubPolyDataFilter::SafeDownCast(_filterMap.Find(Filter_SUB_Sel));
}

//----------------------------------------------------------------------------
IVtkTools_DisplayModeFilter*
QIVtkSelectionPipeline::GetHighlightDMFilter()
{
    return IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Hili));
}

//----------------------------------------------------------------------------
IVtkTools_DisplayModeFilter*
QIVtkSelectionPipeline::GetSelectionDMFilter()
{
    return IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Sel));
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::updatePrimaryPipeline()
{
    _mapper->SetInputConnection(_dataSource->GetOutputPort());
    _mapper->Update();
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::updatePrimaryPipeline(
    IVtk_DisplayMode displayModeFilter)
{

    IVtkTools_DisplayModeFilter* aDMFilter
        = IVtkTools_DisplayModeFilter::SafeDownCast(_filterMap.Find(Filter_DM_Shape));
    aDMFilter->SetInputConnection(_dataSource->GetOutputPort());
    aDMFilter->SetDisplayMode(displayModeFilter);

    _mapper->SetInputConnection(aDMFilter->GetOutputPort());
    _mapper->Update();
}

//----------------------------------------------------------------------------
void QIVtkSelectionPipeline::updatePrimaryPipeline(
    vtkLookupTable* colorsTable)
{
    IVtkTools::InitShapeMapper(_mapper, colorsTable);
}

void QIVtkSelectionPipeline::Modified()
{
    _actor->Modified();
    _hiliActor->Modified();
    _selActor->Modified();
}

void QIVtkSelectionPipeline::SetPipelineVisibility(bool visible)
{
    _actor->SetVisibility(visible);
    _actor->SetPickable(visible);
    _hiliActor->SetVisibility(visible);
    _selActor->SetVisibility(visible);
}

void QIVtkSelectionPipeline::CalculateVertexInfo(const std::vector<Handle(QIVtkSelectionPipeline)>& otherActors)
{
	// 遍历每条边
	TopExp_Explorer edgeExp(_shape, TopAbs_EDGE);
	while (edgeExp.More())
	{
		// 获得 Edge 的端点 + 等分点
		TopoDS_Edge edge = TopoDS::Edge(edgeExp.Current());
		std::vector<gp_Pnt> dividedPoints = getDividedPointsOnEdge(edge, _divisions);
        if (dividedPoints.size() < 2)
        {
            std::cout << "edge divided points less 2? has something wrong. meybe is a closed curve" << std::endl;
            edgeExp.Next();
            continue;
        }

		// 1.添加端点，dividedPoints应该至少包含两个端点
		if (!ContainsVertex(m_points[EVertexType::EdgeEndVertex], dividedPoints[0]))
		{
			m_points[EVertexType::EdgeEndVertex].push_back(dividedPoints[0]);
		}
		if (!ContainsVertex(m_points[EVertexType::EdgeEndVertex], dividedPoints[dividedPoints.size() - 1]))
		{
			m_points[EVertexType::EdgeEndVertex].push_back(dividedPoints[dividedPoints.size() - 1]);
		}
		// 2.添加等分点
		if (dividedPoints.size() > 2)
		{
			m_points[EVertexType::EdgeDividedVertex].insert(m_points[EVertexType::EdgeDividedVertex].end(),
				dividedPoints.begin() + 1, dividedPoints.end() - 1);
		}

		// 3.获得圆心
		gp_Pnt center;
		if (GetArcCenter(edge, center))
		{
			if (!ContainsVertex(m_points[EVertexType::CircleCenterVertex], center))
			{
				m_points[EVertexType::CircleCenterVertex].push_back(center);
			}
		}
		if (GetEllipseCenter(edge, center))
		{
			if (!ContainsVertex(m_points[EVertexType::CircleCenterVertex], center))
			{
				m_points[EVertexType::CircleCenterVertex].push_back(center);
			}
		}

        // 4.获得与其它拓扑形状边的交点
        for (const auto& otherActor : otherActors)
        {
			// 首先判断包围盒是否相交
            if (!IsBoundingBoxesIntersect(edge, otherActor->_shape))
            {
                continue;
            }
            TopExp_Explorer other_edgeExp(otherActor->_shape, TopAbs_EDGE);
            while (other_edgeExp.More())
            {
                auto intersections = GetEdgeIntersections(edge, TopoDS::Edge(other_edgeExp.Current()));
                // 时间换空间，避免重复，但是多了两层循环?
                /*for (const auto& intersection : intersections)
                {
                    if (!ContainsVertex(m_points[EVertexType::IntersectionVertex], intersection))
                    {
                        m_points[EVertexType::IntersectionVertex].push_back(intersection);
                    }
                }*/
                // 空间换时间
                m_points[EVertexType::IntersectionVertex].insert(m_points[EVertexType::IntersectionVertex].end(), 
                    intersections.begin(), intersections.end());
                other_edgeExp.Next();
            }
        }
		edgeExp.Next();
	}

	// 遍历每个面
	TopExp_Explorer faceExp(_shape, TopAbs_FACE);
	while (faceExp.More())
	{
		TopoDS_Face face = TopoDS::Face(faceExp.Current());


		gp_Pnt center;
		if (GetFaceParametricCenter(face, center) && !ContainsVertex(m_points[EVertexType::FaceMidVertex], center))
		{
			m_points[EVertexType::FaceMidVertex].push_back(center);
		}

		faceExp.Next();
	}
}


